כשמדובר בתהליכים מקבילים, המחשבים פשוט מתקפלים ונסוגים לביצוע פעולות בתורות, פעולה אחרי פעולה. באיחוד כאשר זה נוגע לניצול משאבים משותפים, כמו מסד נתונים, כרטיס הרשת או קבצים. ככה קרה שגם הסשן הוא אחד מאותם משאבים משותפים.
הצילו, למה ננעל לי הסשן?
כשפרוצס מנסה לערוך משאב מסוים — הוא נועל אותו. כל זה נעשה כדי למנוע מצב מאוד פשוט - אם שני פרוצסים ינסו לכתוב משהו לסוף הקובץ, הראשון ינסה לרשום 111 והשני ינסה לרשום 222 - התוצאה שנקבל תהיה דומה ל-122112. ברוב המקרים זה לא התוצאה שהיינו רוצים לקבל ולכן אנחנו מוסיפים נעילות, שעוצרות את הפרוצס השני מלגשת אל המשאב, עד שהראשון יסיים.
סשן, שמבוסס על קבצים, חולק גורל זהה לכל קובץ אחר. ברגע ששני סקריפטים מקבילים מנסים לקרוא או לכתוב לשם נתונים, הראשון נועל את הסשן והשני נתקע. ולא רק שאין לנו הרבה שליטה על התהליך, אלה גם טעינת כל דף מורכבת מהמון בקשות לשרת וחלק מהם מופנות אל סקריפטים שמתחילים בשורה session_start
נא להיכנס לפאניקה!
בואו נריץ את שני הסקריפטים הבאים:
test1.php
<?php
session_start();
$_SESSION['test1'] = true;
sleep(20);
echo 'script1 has completed';
session_start();
$_SESSION['test1'] = true;
sleep(20);
echo 'script1 has completed';
test2.php
<?php
session_start();
$_SESSION['test2'] = true;
echo 'script2 has completed';
session_start();
$_SESSION['test2'] = true;
echo 'script2 has completed';
נפעיל את script1 ובזמן שהוא עושה את שלו נפעיל גם את script2.
..כעבור זמן מה..
תופיע הודעת הסיום בסקריפט 2, אבל זה קורה לא לפני, שסקריפט מספר אחד מסיים.
הסיבה מאוד פשוטה - סקריפט2 מנסה לגשת אל קובץ הסשן, מגלה שקובץ הסשן תפוס ומחקה בתור בשקט.
הללויה session_write_close
אחד הפתרונות לבעיה זו היא להגיד ל-PHP שסיימנו, שלא נעשה יותר שינויים לסשן בסקריפט הזה ושאפשר לרשום את הסשן לקובץ ולשחרר את הנעילה. את הפעולה הזו מבצעת הפונקציה session_write_close ובקוד זה נראה ככה:
<?php
session_start();
$_SESSION['test1'] = true;
session_write_close();
sleep(20);
echo 'script1 has completed';
session_start();
$_SESSION['test1'] = true;
session_write_close();
sleep(20);
echo 'script1 has completed';
אבל לא צריך לרוץ לשים את זה בכל סקריפט
קודם כל PHP מבצעת את הפעולה הזו ממילא בסוף הסקריפט לכן לקרוא לפעולה הזו בכוונה בסוף הסקריפט יהיה חסר תועלת. שנית, המקרה הברור שבו רצוי לשים את הפעולה הזו היא ברגע שיש כמה בקשות מקבילות לשרת אל סקריפטים, כמו frames בעמוד או javascriptים, סטיילים ותמונות שנוצרות בצורה דינאמית בשרת, דוגמאת קאפצ'ה.
ואם פתאום גיליתם שמשום מה סקריפטים שאמורים להתבצע במקביל מחכים אחד לשני במקום זאת — סשן זו כנראה הסיבה.
תגובות לכתבה:
אחלה כתבה :)
אהבתי.
תודה רבה לך, רק שאלה קטנה :
הפונקציה sleep זו פונקציה שעוצרת לזמן מסיום ? כמו במקרה שלמעלה זה עוצר ל 20 שניות ?
כן
תודה רבה.
אני זוכר שלפני כמה זמן הייתי מציג (עם curl) להמשתמש לעמוד מסוים אם קרתה שגיאה.
באותו עמוד מסוים הייתי צריך להשתמש בסשן, ומה שקרה זה בעצם שהסקריפט הראשון השתמש בקוקי, שגרם לסקריפט השני (שאליו שלחתי בקשה עם curl) להמתין לסקריפט הראשון שיסיים עם הקוקי, אבל הסקריפט הראשון גם ממתין לסקריפט השני שיחזיר לו תשובה, וככה נכנסתי ללולאה אינסופית ולא היה לי מושג מה לא בסדר. :)
לבסוף הבנתי שזו הבעיה והשתמש בפונקציה. (:
*במקום קוקי -> סשן.